package org.kjs.common.clone.implement;

import org.kjs.common.clone.CloneableConstruct;
import org.kjs.common.clone.exception.CloneNotSupportedException;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Comparator;

/**
 * Created by IntelliJ IDEA.
 * User: KJS
 * Date: 11-8-15
 * Time: 下午9:43
 * To change this template use File | Settings | File Templates.
 */
public class CloneableConstructor<V> implements CloneableConstruct<V> {
    private Class<V> cloneableItemClass;

    protected Constructor<V> getConstructor () {
        Constructor<V> constructor = null;

        if (null != cloneableItemClass) {
            Constructor<V> []  constructors = (Constructor<V>[]) cloneableItemClass.getConstructors();
            Arrays.sort(constructors, new Comparator<Constructor<V>>() {
                public int compare(Constructor<V> c1, Constructor<V> c2) {
                    if (null != c1 && null != c2) {
                        return c2.getModifiers() - c1.getModifiers();
                    } else {
                        return 0;
                    }
                }
            });
            constructor = constructors[0];
        }

        return constructor;
    }

    public V newCloneableInstance() throws CloneNotSupportedException{
        V instance = null;

        if (null != cloneableItemClass) {
            try {
                instance = cloneableItemClass.newInstance();
            } catch (InstantiationException e) {
                instance = null;
            } catch (IllegalAccessException e) {
                instance = null;
            }

            if (null == instance) {
                Constructor<V> constructor = getConstructor();
                if (null != constructor) {
                    constructor.setAccessible(true);
                    Class<?>[] parameterTypes = constructor.getParameterTypes();
                    Object [] parameters = new Object [parameterTypes.length];
                    for (int i=0; i<parameterTypes.length; i++) {
                        Class<?> clazz = parameterTypes[i];
                        String className = clazz.getName();
                        if ("int".equals(className) ||
                                "long".equals(className) ||
                                "short".equals(className) ||
                                "byte".equals(className) ||
                                "double".equals(className) ||
                                "float".equals(className) ||
                                "char".equals(className)) {
                            parameters[i] = (byte)0;
                        } else if ("boolean".equals(className)) {
                            parameters[i] = false;
                        } else {
                            parameters[i] = null;
                        }
                    }
                    try {
                        constructor.newInstance(parameters);
                    } catch (InstantiationException e) {
                        instance = null;
                        throw new CloneNotSupportedException("Construct instance fail.",e);
                    } catch (IllegalAccessException e) {
                        instance = null;
                        throw new CloneNotSupportedException("Construct instance fail.",e);
                    } catch (InvocationTargetException e) {
                        instance = null;
                        throw new CloneNotSupportedException("Construct instance fail.",e);
                    }
                }
            }
        }

        return instance;
    }

    public V newCloneableInstance(Object[] constructorParameters) {
        V instance = null;

        Class<?> [] parameterTypes = new Class<?> [constructorParameters.length];
        for (int i=0; i<constructorParameters.length; i++) {
            Object object = constructorParameters[i];
            if (null != object) {
                parameterTypes[i] = object.getClass();
            } else {
                throw new CloneNotSupportedException("Construct instance fail cause of parameter is null.");
            }
        }
        try {
            instance = cloneableItemClass.getConstructor(parameterTypes).newInstance(constructorParameters);
        } catch (NoSuchMethodException e) {
            instance = null;
            throw new CloneNotSupportedException("Construct instance fail.",e);
        } catch (InvocationTargetException e) {
            instance = null;
            throw new CloneNotSupportedException("Construct instance fail.",e);
        } catch (InstantiationException e) {
            instance = null;
            throw new CloneNotSupportedException("Construct instance fail.",e);
        } catch (IllegalAccessException e) {
            instance = null;
            throw new CloneNotSupportedException("Construct instance fail.",e);
        }
        return instance;
    }

    public Class<V> getCloneableItemClass() {
        return cloneableItemClass;
    }

    public void setCloneableItemClass(Class<V> cloneableItemClass) {
        this.cloneableItemClass = cloneableItemClass;
    }
}
